#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QStandardItemModel>

#include "DeviceInformation.h"

#include <QDebug>
#include <QAction>
#include <QtAlgorithms>
#include <QMenu>

#include <deque>

#pragma comment(lib, "setupapi.lib")

#include "MyDeviceModel.h"
#include "mypacketmodel.h"

std::deque<PPACKET> bufferDeque;

QList<MyPacket*> packetList;
MyPacketModel pModel(&packetList);

QList<DeviceInformation*> deviceList;
MyDeviceModel devModel(&deviceList);

// void MapDeviceVidsPids(const wchar_t *filename);

//////////////////////////////////////////////////////////////////////////

HANDLE hMutex;
bool stopThreads;

QAction *actionFilters[35];
//////////////////////////////////////////////////////////////////////////

extern HANDLE CompletionPort;
extern const char *PacketNames[];

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

	int i;

	for (i=0; i<35; ++i)
	{
		actionFilters[i] = new QAction(PacketNames[i], this);
		actionFilters[i]->setCheckable(true);
		actionFilters[i]->setChecked(true);
		connect(actionFilters[i], SIGNAL(triggered()), this, SLOT(on_actionFilter_clicked()));
		ui->menuFilter->addAction(actionFilters[i]);
	}

	connect(ui->actionStart, SIGNAL(triggered()), this, SLOT(on_startButton_clicked()));
	connect(ui->actionStop,  SIGNAL(triggered()), this, SLOT(on_stopButton_clicked()));

	connect(ui->actionClear, SIGNAL(triggered()), this, SLOT(ClearEventList_clicked()));
	connect(ui->actionRefresh, SIGNAL(triggered()), this, SLOT(RefreshEventList_clicked()));
	connect(ui->actionAddObserved, SIGNAL(triggered()), this, SLOT(AddObserved_clicked()));

    connect(ui->tableView,  SIGNAL(doubleClicked(QModelIndex)), this, SLOT(on_tableView_doubleClicked(QModelIndex)));

    ui->tableView->setModel(&pModel);
    ui->tableView->setColumnWidth(0, 60);
    ui->tableView->setColumnWidth(1, 60);
    ui->tableView->setColumnWidth(2, ui->tableView->width()-140);

	ui->tableView_2->setModel(&devModel);
	ui->tableView_2->setColumnWidth(0, 191);
	ui->tableView_2->setColumnWidth(1, 160);
	ui->tableView_2->setColumnWidth(2, 160);
	ui->tableView_2->setColumnWidth(3, 80);

	packInfDlg = new PacketInformationDialog(this);

	hMutex = CreateMutex( NULL, FALSE, NULL);

	//MapDeviceVidsPids(L"usb.txt");
	EnumerateUsbDevices();	

	stopThreads = true;


	DeviceListenMenu = new QMenu(ui->tableView_2);
	actionStartListenDevice = new QAction("Enable device monitoring", this);
	actionStopListenDevice = new QAction("Disable device monitoring", this);

	DeviceListenMenu->addAction(actionStartListenDevice);
	DeviceListenMenu->addAction(actionStopListenDevice);

	connect(ui->tableView_2, SIGNAL(customContextMenuRequested(const QPoint &)),
			this, SLOT(DeviceContexMenuRequested(const QPoint &)));

	connect(actionStartListenDevice,  SIGNAL(triggered()), this, SLOT(StartListenDeviceClicked()));
	connect(actionStopListenDevice,   SIGNAL(triggered()), this, SLOT(StopListenDeviceClicked()));

	HANDLE hDevice = CreateFile(CONTROL_DEVICE_DOS_NAME, 0, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);

	if(hDevice == INVALID_HANDLE_VALUE)
	{
		QMessageBox::critical(this, "Error",
			"Necessary driver is not loaded");
		delete this;
	}
}

MainWindow::~MainWindow()
{
	DeviceInformation *pDev;

	foreach(pDev, deviceList)
		pDev->StopListen();

	HANDLE hDevice = CreateFile(CONTROL_DEVICE_DOS_NAME, 0, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);
	DWORD dwWaitResult;
	DeviceIoControl(hDevice, IOCTL_NOT_WATCH_NEW_DEVICE, NULL, 0,NULL, 0, &dwWaitResult, 0);
	CloseHandle(hDevice);

    delete ui;
	delete packInfDlg;
}
bool isUsbHubName(WCHAR* nodeName)
{
	WCHAR	rootHubName[] = L"USB\\ROOT_HUB";
	int		rootHubNameLen = wcslen(rootHubName), i;

	for(i = 0; i < rootHubNameLen; i++)
	{
		if(nodeName[i] != rootHubName[i])
			break;
	}

	return rootHubNameLen == i;
}

void MainWindow::on_startButton_clicked()
{
    HANDLE hTh;
    DWORD dwThreadId_1, dwThreadId_2, dwWaitResult, i;

	dwWaitResult = WaitForSingleObject(hMutex, 5000L);
		stopThreads = false;
	ReleaseMutex(hMutex);

	HANDLE hDevice = CreateFile(CONTROL_DEVICE_DOS_NAME, 0, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);

	if(hDevice == INVALID_HANDLE_VALUE)
	{
		QMessageBox::critical(this, "hDevice failure",
			("Error code: " + GetLastError()));
		return;
	}

	DeviceIoControl(hDevice, IOCTL_START_LOG, NULL, 0,NULL, 0, 
			&dwWaitResult, 0);

	CloseHandle(hDevice);

	for (i=0; i<35; ++i)
	{
		actionFilters[i]->setEnabled(false);
	}
	
	//for (i=0; i<10; ++i)
	{
		hTh = CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId_1);
		if (hTh == NULL)
		{
			QMessageBox::critical(this, "CreateThread() failure",
				("Error code: " + GetLastError()));
			CloseHandle(hTh);        
			return;
		}
		SetThreadPriority(hTh, THREAD_PRIORITY_HIGHEST);
		CloseHandle(hTh);
	}
		

	hTh = CreateThread(NULL, 0, PacketGeneratorThread, NULL, 0, &dwThreadId_2);
	if (hTh == NULL)
	{
		QMessageBox::critical(this, "CreateThread() failure",
			("Error code: " + GetLastError()));
		CloseHandle(hTh);        
		return;
	}

	ui->actionStart->setEnabled(false);
	ui->actionStop->setEnabled(true);

}

void MainWindow::on_stopButton_clicked()
{
	DWORD dwWaitResult, i;
    //PostQueuedCompletionStatus(CompletionPort, 0, NULL, NULL);

	dwWaitResult = WaitForSingleObject(hMutex, 5000L);
		stopThreads = true;
	ReleaseMutex(hMutex);

	HANDLE hDevice = CreateFile(CONTROL_DEVICE_DOS_NAME, 0, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);

	if(hDevice == INVALID_HANDLE_VALUE)
	{
		cout<<"fuck";
	}

	DeviceIoControl(hDevice, IOCTL_START_LOG, NULL, 0,NULL, 0, 
			&dwWaitResult, 0);

	CloseHandle(hDevice);

	ui->actionStart->setEnabled(true);
	ui->actionStop->setEnabled(false);

	for (i=0; i<35; ++i)
	{
		actionFilters[i]->setEnabled(true);
	}
}

void MainWindow::on_tableView_doubleClicked(QModelIndex index)
{
    QString text;
    MyPacket *pPac = packetList[index.row()];

	text = QString::fromStdWString(pPac->GetDetailedInfo());

    packInfDlg->SetHTML(text);
    packInfDlg->show();
}

void MainWindow::on_actionFilter_clicked()
{
	
}

void MainWindow::EnumerateUsbDevices()
{
	HDEVINFO			deviceInfo;
	SP_DEVINFO_DATA     deviceInfoData;
	ULONG				index;
	DWORD				requiredLength;
	PDWORD				DataT = NULL;
	HANDLE				hDevice;
	DWORD				dwWaitResult;

	WCHAR deviceId[MAX_HARDWARE_NAME_LEN];
	WCHAR deviceDescriptor[MAX_HARDWARE_NAME_LEN];
	

	// Iterate over some Host Controller names and try to open them.
	//
	deviceInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_USB,
					L"USB",
					NULL,
					DIGCF_PRESENT);

	if (deviceInfo != INVALID_HANDLE_VALUE)
	{
		deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
		hDevice = CreateFile(CONTROL_DEVICE_DOS_NAME, 0, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);

		if(hDevice == INVALID_HANDLE_VALUE)
		{
			cout<<"INVALID_HANDLE_VALUE";
			return;

		}

		for (index=0; SetupDiEnumDeviceInfo(deviceInfo, index, &deviceInfoData); index++)
		{
			SetupDiGetDeviceRegistryProperty(
				deviceInfo,
				&deviceInfoData,
				SPDRP_DEVICEDESC,
				NULL,
				NULL,
				NULL,
				&requiredLength);

			if(SetupDiGetDeviceRegistryProperty(
				deviceInfo,
				&deviceInfoData,
				SPDRP_DEVICEDESC,
				DataT,
				(PBYTE)deviceDescriptor,
				requiredLength,
				&requiredLength))
			{
				char isDeviceObserved;
				

				if(CM_Get_Device_ID(deviceInfoData.DevInst, deviceId, MAX_HARDWARE_NAME_LEN, 0) == CR_SUCCESS)
				{
					if(!isUsbHubName(deviceId))
					{
						DeviceIoControl(hDevice, IOCTL_IS_WATCH_DEVICE, deviceId, 
								sizeof(deviceId), &isDeviceObserved, 1, &dwWaitResult, 0);

						deviceList.append(new DeviceInformation(wstring(deviceDescriptor), deviceId, isDeviceObserved));
					}
				}
				
			}
		}
		CloseHandle(hDevice);
	}
	//  Cleanup
	SetupDiDestroyDeviceInfoList(deviceInfo);
}

void MainWindow::ClearEventList_clicked()
{
	qDeleteAll(packetList);
	packetList.clear();
	pModel.Update();
}

void MainWindow::RefreshEventList_clicked()
{
	qDeleteAll(deviceList);
	deviceList.clear();
	EnumerateUsbDevices();
	devModel.Update();
}

void MainWindow::AddObserved_clicked()
{
	HANDLE hDevice = CreateFile(CONTROL_DEVICE_DOS_NAME, 0, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);
	DWORD dwWaitResult;

	if(hDevice == INVALID_HANDLE_VALUE)
	{
		cout<<"INVALID_HANDLE_VALUE";
		return;
	}

	if(ui->actionAddObserved->isChecked())
		DeviceIoControl(hDevice, IOCTL_WATCH_NEW_DEVICE, NULL, 0,NULL, 0, &dwWaitResult, 0);
	else
		DeviceIoControl(hDevice, IOCTL_NOT_WATCH_NEW_DEVICE, NULL, 0,NULL, 0, &dwWaitResult, 0);

	CloseHandle(hDevice);
}


void MainWindow::DeviceContexMenuRequested(const QPoint &pos)
{
	deviceListIndex = ui->tableView_2->indexAt(pos);
	//deviceListIndex
	if (!deviceListIndex.isValid())
	{
		return;
	}
	bool isSelDevListened = deviceList[deviceListIndex.row()]->bIsListened ?
		true : false;

	if (isSelDevListened)
	{
		actionStartListenDevice->setEnabled(false);
		actionStopListenDevice->setEnabled(true);
	}
	else
	{
		actionStartListenDevice->setEnabled(true);
		actionStopListenDevice->setEnabled(false);
	}

	DeviceListenMenu->exec(QCursor::pos());
}

void MainWindow::StartListenDeviceClicked()
{
	if (!deviceListIndex.isValid())
	{
		return;
	}

	DeviceInformation *pDev = deviceList[deviceListIndex.row()];
	pDev->bIsListened = true;
	pDev->StartListen();

	devModel.Update();
}

void MainWindow::StopListenDeviceClicked()
{
	if (!deviceListIndex.isValid())
	{
		return;
	}

	DeviceInformation *pDev = deviceList[deviceListIndex.row()];
	pDev->bIsListened = false;
	pDev->StopListen();

	devModel.Update();
}